/*
 * SessionManager.cpp
 *
 *  Created on: Mar 9, 2017
 *      Author: ectrs
 */

#include <string>
#include <Poco/Util/Timer.h>
#include <Poco/Util/TimerTask.h>
#include <Poco/Util/TimerTaskAdapter.h>
#include <Poco/Util/Application.h>
#include <Poco/JSON/Object.h>
#include <Poco/DynamicStruct.h>
#include <Poco/UUIDGenerator.h>
#include <Poco/Logger.h>

using namespace Poco;
using namespace Poco::JSON;
using namespace Poco::Util;
using namespace Poco::Dynamic;

#include "Helper.h"
#include "SessionManager.h"

std::vector<MySession> SessionManager::_vecSessions;
DatagramSocket SessionManager::_socket(Poco::Net::IPAddress::IPv4);
Timer SessionManager::_timer;

bool SessionManager::initialize(ENUM_SOCKET_MODE mode)
{
	bool blRet = false;
	if (mode == SOCKET_MODE_SERVER)
	{
		SocketAddress sa(IPAddress(), 5004);
		_socket.bind(sa, true);
		poco_information(Application::instance().logger(), "SessionManager server socket created.");
	}
	else
	{
		Poco::Net::SocketAddress sa(IPAddress(), 5004);
		_socket.connect(sa);
		poco_information(Application::instance().logger(), "SessionManager client socket connected.");
	}
	return blRet;
}

int SessionManager::receiveFrom(char* buffer, int length, SocketAddress& address)
{
	int n = _socket.receiveFrom(buffer, length, address);
	buffer[n] = 0;
	poco_information(Application::instance().logger(), "received from libSessionManager msg(" + std::to_string(n) + "): " + (char*)buffer);

	return n;
}

int SessionManager::receiveBytes(char* buffer, int length)
{
	int n = _socket.receiveBytes(buffer, length);
	buffer[n] = 0;
	poco_information(Application::instance().logger(), "received from libSessionManager msg(" + std::to_string(n) + "): " + (char*)buffer);

	return n;
}

int SessionManager::sendTo(const char* buffer, int length, SocketAddress& address)
{
	int n =  _socket.sendTo(buffer, length, address);
	poco_information(Application::instance().logger(), "sent to libSessionManager msg(" + std::to_string(n) + "): " + (char*)buffer);

	return n;
}

int SessionManager::sendBytes(const char* buffer, int length)
{
	int n = _socket.sendBytes(buffer, length);
	poco_information(Application::instance().logger(), "sent to libSessionManager msg(" + std::to_string(n) + "): " + (char*)buffer);

	return n;
}

UUID SessionManager::getFreeUUID()
{
	UUIDGenerator& gen = UUIDGenerator::defaultGenerator();

	UUID uuid;
	MySession session;
	std::string sid;
	do {
		uuid = gen.createRandom();
		sid = uuid.toString();
	} while (findSessionWithSid(sid, session));

	return uuid;
}

void SessionManager::doLogin(const Var params, std::string& strResp)
{
	Object::Ptr joParams = params.extract<Object::Ptr>();

	std::string user = joParams->getValue<std::string>("user");
	std::string passwd = joParams->getValue<std::string>("passwd");

	Object joResp;
	MySession session;
	//if (checkUserPasswd(user, passwd))
	if (session.login(user, passwd))
	{
		UUID uuid = getFreeUUID();
		session.setUuid(uuid);

		registSession(session);

		joResp.set("result", "success");
		joResp.set("sid", session.getUuid().toString());
	}
	else
	{
		joResp.set("result", "failed");
		joResp.set("reason", "user or passwd is error");
	}
	strResp = ((DynamicStruct)joResp).toString();
}

void SessionManager::doLogout(const Var params, std::string& strResp)
{
	Object::Ptr joParams = params.extract<Object::Ptr>();

	std::string sid = joParams->getValue<std::string>("sid");

	Object joResp;
	MySession session;
	if (findSessionWithSid(sid, session))
	{
		session.logout();
		if (unregistSession(session) == true)
		{
			joResp.set("result", "success");
		}
		else
		{
			joResp.set("result", "failed");
			joResp.set("reason", "sid not exist");
		}
	}
	else
	{
		joResp.set("result", "failed");
		joResp.set("reason", "user or passwd is error");
	}
	strResp = ((DynamicStruct)joResp).toString();
}

void SessionManager::doQuery(const Var params, std::string& strResp)
{
	Object::Ptr joParams = params.extract<Object::Ptr>();

	std::string sid = joParams->getValue<std::string>("sid");

	Object joResp;
	MySession session;
	if (findSessionWithSid(sid, session))
	{
		joResp.set("result", "success");
	}
	else
	{
		joResp.set("result", "session not found");
	}
	strResp = ((DynamicStruct)joResp).toString();
}

bool SessionManager::findSessionWithSid(std::string& sid, MySession& session)
{
	bool blRet = false;

//	poco_information(*pLogger, sid);
	//UUID uuid = UUID(sid);
	for (auto item : _vecSessions)
	{
//		poco_information(*pLogger, item.getUuid().toString());
		if (item.getUuid().toString() == sid)
		{
//			poco_information(*pLogger, "found");
			session = item;
			blRet = true;
			break;
		}
	}

	return blRet;
}

bool SessionManager::registSession(MySession& session)
{
	bool blRet = true;

	_vecSessions.push_back(session);

	TimerTask::Ptr pTask = new TimerTaskAdapter<MySession>(session, &MySession::onTimer);
	//_timer.schedule(pTask, 10 * 60 * 1000, 0);
	_timer.schedule(pTask, 6 * 1000, 0);

	return blRet;
}

bool SessionManager::unregistSession(MySession& session)
{
	bool blRet = false;

	auto beg = _vecSessions.begin();
	for (; beg != _vecSessions.end(); ++beg)
	{
		if (beg->getUuid().toString() == session.getUuid().toString())
			break;
	}
	if (beg != _vecSessions.end())
	{
		_vecSessions.erase(beg);
		blRet = true;
	}

	return blRet;
}

void SessionManager::timeout(MySession& session)
{
	unregistSession(session);
}
